#ifndef STM32F750_LOGGER_H
#define STM32F750_LOGGER_H
#ifdef ARDUINO

#include <Arduino.h>
#include "stm32_def.h"
#include <core_debug.h>

#endif

#include "smodule.h"
#include "ArduinoLogger.h"
#include "rtos.h"

#ifdef USE_KVDB

#include "tsdb.h"

#endif

#include <cstdio>
#include <cstdlib>

#define BUFFER_SIZE_NRS 512
//与LWIP定义冲突
#undef read
#undef write
#undef close
#if !defined(LINUX)
typedef unsigned long long u64;
#endif

int serial_putchar(char c);

class logger_t final : public LoggerBase, public smodule {
private:
    size_t BUFFER_SIZE = BUFFER_SIZE_NRS;
    void *lock = nullptr;
    u32 output{};

public:
    enum LOGGER {
        LOGGER_ITM = 0x1,
        LOGGER_SERIAL = 0x2,
        LOGGER_TSL = 0x4
    };

    /// Default constructor
    logger_t() = default;

    char *buf;

    int begin(u32 tick) override {
        begin((LOGGER) (LOGGER_ITM | LOGGER_SERIAL));
        return 0;
    }

    void begin(LOGGER _output) {
        output = _output;
        level(log_level_e::debug);
        lock = rtos::Create_Mutex();
        buf = (char *) malloc(256);
    };

    void enable(LOGGER c) {
        output |= c;
    }

    void disable(LOGGER c) {
        output &= ~c;
    }

    /// Default destructor
    ~logger_t() override = default;

    void flush()

    noexcept override;

    void clear()

    noexcept override{
            counter = 0;
    }

    void log_customprefix()

    noexcept override{
            // printf("[%d ms] ", rtos::ticks());
    }

    template<typename... Args>
    void log(log_level_e l, const char *fmt, const Args &...args)

    noexcept {
        rtos::mutex_lock(lock);
        if (enabled_ && l <= level_) {
            // Add our prefix

            int len = snprintf(buf, 256, fmt, std::forward<const Args>(args)...);

            print("%s", LOG_LEVEL_TO_SHORT_C_STRING(l));

            // log_customprefix();

            // Send the primary log statement
#ifdef ARDUINO_ARCH_STM32
            if (output & LOGGER_ITM)
                for (int i = 0; i < len; i++)
                    ITM_SendChar(buf[i]);
#endif
            print(buf);
            log_putc('\n');
#ifdef USE_KVDB
            if (output & LOGGER_TSL) {
                tsdb.append("log", buf);
            }
#endif
        }
        rtos::mutex_unlock(lock);
    }

    template<typename... Args>
    void critical(const char *fmt, const Args &...args) {

        log(log_level_e::critical, fmt, std::forward<const Args>(args)...);
    }

    template<typename... Args>
    void error(const char *fmt, const Args &...args) {

        log(log_level_e::error, fmt, std::forward<const Args>(args)...);
    }

    template<typename... Args>
    void warning(const char *fmt, const Args &...args) {

        log(log_level_e::warning, fmt, std::forward<const Args>(args)...);
    }

    template<typename... Args>
    void info(const char *fmt, const Args &...args) {

        log(log_level_e::info, fmt, std::forward<const Args>(args)...);
    }

    template<typename... Args>
    void debug(const char *fmt, const Args &...args) {
        log(log_level_e::debug, fmt, std::forward<const Args>(args)...);
    }

    int run(u32 tick) override {
        //
        return 0;
    }

    int diag(u32 tick) override {
        return 0;
    }

private:
    void log_putc(char c)

    noexcept override{
            buffer_[counter] = c;
            counter++;
#ifdef ARDUINO
            if (output & LOGGER_ITM)
                ITM_SendChar(c);
            if (output & LOGGER_SERIAL) {
                serial_putchar(c);
            }
#else
            printf("%c", c);
#endif
            if (counter == BUFFER_SIZE) {
                flush();
            }
    }

    static void errorHalt(const char *msg) {
#ifdef ARDUINO
        core_debug("Error: %s\n", msg);
#else
        printf("Error: %s\n", msg);
#endif
    }

    size_t counter = 0;
    char buffer_[BUFFER_SIZE_NRS]{};
};

extern logger_t logger;

#endif // STM32F750_LOGGER_H
